<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Utils\Cache;

use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\Redis;

/**
 * Redis缓存驱动
 * 要求安装phpredis扩展.
 */
class RedisUtil
{
    #[Inject]
    protected Redis $handler;

    /**
     * 设置缓存.
     * @param mixed $keyFormat redis key的格式
     * @param mixed $value     redis key的值
     * @param null|mixed $ttl  redis key的过期时间
     * @param null|mixed $keyValues  redis key的格式化的值
     * @return mixed
     */
    public function set($keyFormat, $value, $ttl = null, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        if (is_null($ttl)) {
            $result = $this->handler->set($key, $value);
        } else {
            $result = $this->handler->setex($key, $ttl, $value);
        }
        return $result;
    }

    /**
     * 读取缓存.
     * @param mixed $keyFormat
     * @return mixed
     */
    public function get($keyFormat, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        $value = $this->handler->get($key);
        if ($value === false) {
            return null;
        }
        $jsonData = json_decode($value, true);
        return ($jsonData === null) ? $value : $jsonData;   // 检测是否为JSON数据 true 返回JSON解析数组, false返回源数据
    }

    /**
     * 设置缓存.
     * @param mixed $keyFormat redis key的格式
     * @param null|mixed $ttl  redis key的过期时间
     * @param null|mixed $keyValues  redis key的格式化的值
     * @return mixed
     */
    public function expire($keyFormat,$ttl, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        $result = $this->handler->expire($key, $ttl);
        return $result;
    }

    /**
     * 设置锁
     * User: tc
     * Date: 2024/12/31 4:57 下午.
     *
     * @param int $ttl
     * @param mixed $keyFormat
     * @param mixed $flag
     * @return string
     */
    public function setLock($keyFormat, $ttl = 10, $flag = 'ex', ...$keyValues)
    {
        mt_srand();
        $uniKey = uniqid() . mt_rand(1000, 9999);
        $key = $this->format($keyFormat, $keyValues);
        $isOk = $this->handler->set($key, $uniKey, ['nx', $flag => $ttl]);
        if (! $isOk) {
            return false;
        }
        return $uniKey;
    }

    /**
     * 设置锁,没获得一直等待
     * User: tc
     * Date: 2024/12/31 4:57 下午.
     *
     * @param int $ttl
     * @param mixed $keyFormat
     * @param mixed $flag
     * @return string
     */
    public function setWaitLock($keyFormat, $ttl = 10, $flag = 'ex', ...$keyValues)
    {
        mt_srand();
        $uniKey = uniqid() . mt_rand(1000, 9999);
        $key = $this->format($keyFormat, $keyValues);
        while (!$this->handler->set($key, $uniKey, ['NX', $flag => $ttl])) {
            // 如果设置失败，等待一段时间后重试
            usleep(1000);
        }
        return $uniKey;
    }

    /**
     * 释放锁
     * User: tc
     * Date: 2024/12/31 4:57 下午.
     *
     * @param mixed $keyFormat
     * @return bool|mixed
     */
    public function delLock($keyFormat, $lockKey, ...$keyValues)
    {
        if (empty($lockKey)) {
            return true;
        }
        $key = $this->format($keyFormat, $keyValues);
        $lua = <<<'EOF'
            if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end
EOF;

        return $this->handler->eval($lua, [$key, $lockKey], 1);
    }

    /**
     * 删除缓存.
     * @param string $keyFormat 缓存模板
     * @return bool
     */
    public function remove($keyFormat, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        return $this->handler->del($key);
    }

    /**
     * 删除缓存.
     * @param string $pattern 缓存模板
     * @return bool
     */
    public function removeByPattern($pattern)
    {
        $keys = $this->handler->keys($pattern);
        return $this->handler->del($keys);
    }

    public function incr($keyFormat, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        return $this->handler->incr($key);
    }

    public function decr($keyFormat, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        return $this->handler->decr($key);
    }

    // redis key值格式化
    private function format($keyFormat, ...$keyValues)
    {
        if (count($keyValues[0]) == 0) {
            return $keyFormat;
        }
        $re = preg_match_all('/\\{[A-Z|a-z|0-9|_]+\\}/', $keyFormat, $result);
        if ($re !== false) {
            return str_replace($result[0], $keyValues[0], $keyFormat);
        }
        return $keyFormat;
    }

    /**
     * 从右插入
     * @param mixed $keyFormat redis key的格式
     * @param mixed $value     redis key的值
     * @param null|mixed $keyValues  redis key的格式化的值
     * @return mixed
     */
    public function lPush($keyFormat,$value, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        return $this->handler->lpush($key,$value);
    }

    /**
     * 从右弹出
     * @param mixed $keyFormat redis key的格式
     * @param mixed $value     redis key的值
     * @param null|mixed $keyValues  redis key的格式化的值
     * @return mixed
     */
    public function rPop($key)
    {
        return $this->handler->rpop($key);
    }

    /**
     * 设置HASH缓存.
     * @param mixed $keyFormat redis key的格式
     * @param mixed $value     redis key的值
     * @param null|mixed $keyValues  redis key的格式化的值
     * @return mixed
     */
    public function hMset($keyFormat, $value, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        $result = $this->handler->hMset($key, $value);
        return $result;
    }

    /**
     * 获取HASH缓存.
     * @param mixed $keyFormat redis key的格式
     * @param mixed $value     redis key的值
     * @param null|mixed $keyValues  redis key的格式化的值
     * @return mixed
     */
    public function hMget($keyFormat,$hashKey, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        if(is_string($hashKey)){
            $result = $this->handler->hGet($key, $hashKey);
        }else{
            $result = $this->handler->hMget($key,$hashKey);
        }
        return $result;
    }


    /**
     * 设置集合缓存.
     * @param mixed $keyFormat redis key的格式
     * @param mixed $value     redis key的值
     * @param null|mixed $keyValues  redis key的格式化的值
     * @return mixed
     */
    public function sAdd($keyFormat, $value, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        $result = $this->handler->sAdd($key, $value);
        return $result;
    }

    /**
     * 获取set集合的成员数
     * @param mixed $keyFormat redis key的格式
     * @param null|mixed $keyValues  redis key的格式化的值
     * @return mixed
     */
    public function sCard($keyFormat, ...$keyValues)
    {
        $key = $this->format($keyFormat, $keyValues);
        $result = $this->handler->sCard($key);
        return $result;
    }
}
